﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Day06
{
    class Program
    {
        //异步调用
        static void Main1()
        {
            ////同步调用：排队执行
            //string result =  ArrivingMessage();
            //Console.WriteLine("收到" + result);
            //Console.ReadLine();

            ////委托实现异步调用
            ////1.使用委托包装需要异步调用的方法
            ////Func<string> handler = new Func<string>(ArrivingMessage);
            //Func<string> handler = ArrivingMessage;
            ////2.开启异步调用
            //handler.BeginInvoke(null,null);
            //Console.ReadLine();

            Func<string> handler = ArrivingMessage;
            //BeginInvoke第一个参数：回调函数；
                        //执行时机：ArrivingMessage执行完毕后
                        //作用：常常用于接收返回值
            //public delegate void AsyncCallback(IAsyncResult ar);
            IAsyncResult ar = handler.BeginInvoke(iar => 
            {
                //EndInvoke会阻塞线程，直到ArrivingMessage执行完毕
                string result = handler.EndInvoke(iar);
                Console.WriteLine("收到" + result);
            }, null);
            //目的：接收返回值

            ////EndInvoke会阻塞线程，直到ArrivingMessage执行完毕
            //string result = handler.EndInvoke(ar);
            //Console.WriteLine("收到" + result);

            Console.ReadLine();
        }

        private static string ArrivingMessage()
        {
            //当前线程睡眠3秒
            //此时用于模拟程序紧张的计算过程
            System.Threading.Thread.Sleep(3000);
            Console.WriteLine("完成");
            return "ok";
        }
        //多播委托和事件
        static void Main2()
        {
            MainCity city = new MainCity();
            //委托：一类（一个委托包含多个方法）行为抽象——不关心方法，方法名可以不同
            //接口：一组（接口可以包含多类方法）行为抽象——实现类方法名相同
            //抽象类：一个概念（可以包含抽象方法和普通成员）抽象——子类方法名相同
            //普通类：一个概念（可以包含虚方法和普通成员）抽象——子类方法名相同

            //单播委托：一个委托绑定一个方法
            city.OnDamage = Alarm.Open;
            //多播委托：一个委托绑定多个方法
            city.OnDamage += Enclosure.Upgrade;//用+增加多个委托方法，用-减少多个委托方法//city.OnDamage -= Alarm.Open;
            city.Damage();//受伤通知多个对象
                          //city.OnDamage = null;//清空委托链
                          //city.OnDamage();//直接通知多个对象
            Console.WriteLine("**************分隔**************");
            //缺点：
            //1.多播委托绑定方法时，第一个使用=，其余使用+=，带来维护困难
            //2.提供直接调用委托、清空委托链等无需对外提供的功能；违反封装思想
            //解决方案：使用事件代替多播委托
            MainCity city1 = new MainCity() { Hp = 100, MaxHp = 100, Name = "1号城堡"};
            //city.OnDamageEventHandler = Alarm.Open;//错误，不能直接引发事件，只能用+=或-=引发事件
            city1.OnDamageEventHandler += Alarm.Open;//尽管绑定第一个委托，也是用+=
            city1.OnDamageEventHandler += Enclosure.Upgrade;//注册
            city1.Damage();//受伤通知多个对象
            //city.OnDamageEventHandler = null;//不能清空事件
            //city.OnDamageEventHandler();//不能直接引发事件
        }
        //委托原理
        static void Main3(string[] args)
        {
            //通过ILSpy反编译看IL中间代码
            Action a = new Action(Main1);
            //1.创建新委托对象指向Main2方法
            a += Main2;
            a();

            Func<int> b = () => { return 1; };
            b += () => { return 2; };
            b += () => { return 3; };
            int re = b();//调用委托 获取委托链最后一个方法返回值
            Console.WriteLine(re) ;
            //获取所有方法返回值
            //遍历委托链
            foreach (var item in b.GetInvocationList())
            {
                object result = item.DynamicInvoke();//逐一引发
            }

        }
        //反射
        static void Main4(string[] args)
        {
            //在编辑期间就确定的
            MainCity city = new MainCity();
            city.Name = "2号城堡";
            city.Hp = 100;
            city.PrintHp();
            //city.Fun1(); 不能调用私有方法,反射可以

            //反射，在运行期间确定的
            //1.获取类型
            Type type = Type.GetType("Day06.MainCity");
            Type type1 = city.GetType();
            Type type2 = typeof(MainCity);
            //2.创建对象
            Object obj = Activator.CreateInstance(type);
            PropertyInfo namePro = type.GetProperty("Name");
            namePro.SetValue(obj,"1号城堡");
            PropertyInfo hpPro = type.GetProperty("Hp");
            //动态转换类型：
            object hpValue = Convert.ChangeType("120", hpPro.PropertyType);
            //hpPro.SetValue(obj, 120);
            hpPro.SetValue(obj, hpValue);

            MethodInfo printHp_Method = type.GetMethod("PrintHp");
            printHp_Method.Invoke(obj,null);

            //调用私有方法（搜索方式：非公开、实例）
            MethodInfo funMethod = type.GetMethod("Fun1",BindingFlags.NonPublic | BindingFlags.Instance);//BindingFlags.NonPublic搜索条件,枚举多选按位或
            funMethod.Invoke(obj,null);
        }
        //练习：
        //Json字符串：用于描述对象状态
        //格式：{"属性":"值"，"属性":"值"}
        //1.对象——>Json串的方法
        //思路：反射该对象所有属性，根据名称拼接字符串，用stringBuilder拼
        //2.Json串——>对象的方法
        //思路：根据泛型获取目标类型，创建对象。T t = new T();
        //根据json串中属性，为对象赋值。
        //弃用
        static void Main5(string[] args)
        {
            MainCity city = new MainCity();
            city.Name = "1号城堡";
            city.Hp = 100;

            string json = MyObjectToString(city);
            Console.WriteLine("对象——>Json串:{0}", json);

            //string json1 = "{ \"Hp\":\"80\",\"MaxHp\":\"100\",\"Name\":\"3号城堡\"}";
            MainCity objByJson = MyStringToObject<MainCity>(json);
            Console.WriteLine("对象名:" + objByJson + " 属性" + " Hp:" + objByJson.Hp + " MaxHp:" + objByJson.MaxHp + " Name:" + objByJson.Name);//调试查看objByJson的属性值

            //List<int> arr2 = new List<int>(10);//建议List添加10初始化空间

            //错误示例
            //string str = string.Empty;
            //for (int i = 0; i < 10; i++)
            //{
            //    //每次拼接创建新对象
            //    str += i.ToString();
            //}
            //Console.WriteLine(str);//输出：0123456789
        }
        //弃用
        private static string MyObjectToString(Object obj)
        {
            StringBuilder builder = new StringBuilder(10);
            Type type = obj.GetType();
            PropertyInfo[] allPros = type.GetProperties();
            builder.Append("{");
            for (int i = 0; i < allPros.Count(); i++)
            {
               
                builder.Append("\"" + allPros[i].Name + "\"" + ":" + "\"" + allPros[i].GetValue(obj) + "\",");
            }
            builder.Remove(builder.Length - 1, 1);
            builder.Append("}");
            return builder.ToString();
        }
        //弃用
        private static T MyStringToObject<T>(string json)
        {
            //json = "{\"Hp\":\"80\",\"MaxHp\":\"100\",\"Name\":\"3号城堡\"}";
            string[] jsonArr = json.Split(',');
            for (int i = 0; i < jsonArr.Length; i++)
            {
                if (i == 0)
                {
                    jsonArr[i] = jsonArr[i].Remove(0,1);
                }
                if (i == jsonArr.Length - 1 )
                {
                    jsonArr[i] = jsonArr[i].Replace("}", "");
                }
                jsonArr[i] = jsonArr[i].Replace("\"","");
            }
            Type type = typeof(T);
            T obj = (T)Activator.CreateInstance(type);
            PropertyInfo[] allPros = type.GetProperties();
            if (allPros!=null)
            {
                for (int i = 0; i < allPros.Count(); i++)
                {
                    for (int j = 0; j < jsonArr.Length; j++)
                    {
                        if (jsonArr[i].Split(':')[0] == allPros[i].Name)
                        {
                            allPros[i].SetValue(obj, 
                                Convert.ChangeType(jsonArr[i].Split(':')[1], allPros[i].PropertyType));
                        }
                    }
                }
            }
            return obj;
        }

        static void Main6(string[] args)
        {
            MainCity city = new MainCity();
            city.Name = "1号城堡";
            city.Hp = 100;

            string json = JsonHelper.ObjectToString(city);
            Console.WriteLine(json);
            var obj = JsonHelper.StringToObject<MainCity>(json);
            Console.WriteLine("对象名:" + obj + " 属性" + " Hp:" + obj.Hp + " MaxHp:" + obj.MaxHp + " Name:" + obj.Name);
        }
        static void Main(string[] args)
        {
            string[,] array = new string[4, 4];
            for (int r = 0; r < 4; r++)
            {
                for (int c = 0; c < 4; c++)
                {
                    array[r, c] = r.ToString() + c.ToString();
                }
            }
            /*
             * 00 01 02 03
             * 10 11 12 13
             * 20 21 22 23
             * 30 31 32 33
             * */

            //获取某个元素某个方向指定数量的元素。
            //     10      右侧      3  —— 拿到11 12 13
            //技巧：0 1表示右侧；1 0表示下侧；-1 -1表示左上……
            string[] re1 = DoubleArraryHelper.GetElements(array, 1, 0, 0, 1, 3);
            //过渡版
            string[] re2 = array.GetElements(1, 0, 0, 1, 3);//静态类且方法参数加this后这么调用，过渡方法
            //最终版
            string[] re3 = array.GetElements(1, 0, Direction.Right, 3);//Direction的好处在调用的地方

            /*
             结构：封装小型相关变量的数据类型。
             特点：与类语法相似，都可以包含数据成员（字段），与方法成员（属性、构造函数、方法）
                   最大的区别，在于结构是值类型，类时引用类型
             适用性：数据成员较少。比如：方向、位置、旋转、颜色……
             */
            Direction dir = Direction.Right;//0 1
            dir = Direction.Up;//-1 0 
            //如果是类，则创建新对象，替换引用，产生垃圾；
            //如果是结构，则修改栈中存储的数据，没有产生垃圾。
            dir = new Direction();//结构体中一定送无参数构造函数，所以不能再包含无参数构造函数
        }
    }
}
